/* -*- pse-c -*-
 *-----------------------------------------------------------------------------
 * Filename: oswindows.cpp
 * $Revision: 1.95.98.2.6.1 $
 *-----------------------------------------------------------------------------
 * INTEL CONFIDENTIAL
 * Copyright (2002-2008) Intel Corporation All Rights Reserved.
 * The source code contained or described herein and all documents related to
 * the source code ("Material") are owned by Intel Corporation or its suppliers
 * or licensors. Title to the Material remains with Intel Corporation or its
 * suppliers and licensors. The Material contains trade secrets and proprietary
 * and confidential information of Intel or its suppliers and licensors. The
 * Material is protected by worldwide copyright and trade secret laws and
 * treaty provisions. No part of the Material may be used, copied, reproduced,
 * modified, published, uploaded, posted, transmitted, distributed, or
 * disclosed in any way without Intel's prior express written permission.
 * 
 * No license under any patent, copyright, trade secret or other intellectual
 * property right is granted to or conferred upon you by disclosure or
 * delivery of the Materials, either expressly, by implication, inducement,
 * estoppel or otherwise. Any license under such intellectual property rights
 * must be express and approved by Intel in writing.
 * 
 * 
 *-----------------------------------------------------------------------------
 * Description:
 *  This file has the code for communicating with the Windows OS and the IEGD
 *  driver through the use of Escapes/IOCTLs.
 *-----------------------------------------------------------------------------
 * Authors:
 *  
 *-----------------------------------------------------------------------------
 */

#include "oswindows.h"
#include "../include/dbgprint.h"
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include <iostream>

bool g_driver_present = false;
bool g_simulation_mode = false;
HMODULE g_lib_hdl = NULL;

HMODULE user32_hmodule=NULL;
PFNCHANGEDISPLAYSETTINGSEX pfnChangeDisplaySettingsEx = NULL;



/*-----------------------------------------------------------------------------
 * Function:
 *		open_device
 * Parameters:
 *		OUT hdl - the hdl created after calling CreateFile
 *		IN devicename - the device name (string) to open
 * Description:
 *		Open a device using CreateFile
 * Returns:
 *		bool
 *			true  - indicates success
 *			false - indicates failure
 *-----------------------------------------------------------------------------
 */
bool open_device(OUT HANDLE *hdl, IN char *device_name)
{
	char dev_full_name[MAX_SIZE];
	bool ret_val;

	sprintf(dev_full_name, "\\\\.\\%s", device_name);

	*hdl = CreateFile(dev_full_name,
		GENERIC_READ|GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		0,
		NULL);

	if (*hdl == INVALID_HANDLE_VALUE) {

		display_error_message();
		ret_val = false;

	} else {

		ret_val = true;

	}

	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		close_device
 * Parameters:
 *		IN hdl - the hdl created after calling CreateFile
 *		IN devicename - the device name (string) to close
 * Description:
 *		Close a device using CloseHandle API
 * Returns:
 *		void
 *-----------------------------------------------------------------------------
 */
void close_device(IN HANDLE hdl, IN char *devicename)
{
	CloseHandle(hdl);
}


/*-----------------------------------------------------------------------------
 * Function:
 *		os_initialize
 * Parameters:
 *		IN bool is_driver_in_binary - This parameter tells this function
 *							whether a driver is present in the caller's
 *							binary. Only if it's present should we extract
 *							and install it.
 * Description:
 *		This function will extract and load a driver that we will need
 *		to use for major functionality of this application.
 * Returns:
 *		bool -
 *			true  = success
 *			false = failure
 *-----------------------------------------------------------------------------
 */
bool os_initialize(IN bool is_driver_in_binary)
{
	bool ret_val = true;
	BOOTUP_FUNC bootup_sequence_ptr;

	/* If the driver that we will use is not present, then we
		need to extract and load it */
	if(is_driver_in_binary && !g_driver_present) {
		if(!LOAD_DRIVER()) {

			ret_val = false;
		} else {

			g_driver_present = true;
		}
	}

	g_lib_hdl = LoadLibrary(TEST_MINI_DLL);

	g_simulation_mode = (g_lib_hdl) ? true : false;

	if(g_lib_hdl) {

		bootup_sequence_ptr = (BOOTUP_FUNC) GetProcAddress(g_lib_hdl,
			BOOTUP_FUNC_NAME);

		unsigned long bootup_ret;
		bootup_ret = bootup_sequence_ptr();

		ret_val = (bootup_ret == 0) ? true : false;
	}
	user32_hmodule = LoadLibrary("user32.dll");
	if (user32_hmodule) {
		pfnChangeDisplaySettingsEx = (PFNCHANGEDISPLAYSETTINGSEX)
		GetProcAddress(user32_hmodule, CHANGE_DISPLAY_SETTINGS_EX);
	}

	return ret_val;
}


/*-----------------------------------------------------------------------------
 * Function:
 *		os_uninitialize
 * Parameters:
 *		NONE
 * Description:
 *		This function will unload the device driver that we installed
 *		in the os_initialize function.
 * Returns:
 *		bool -
 *			true  = success
 *			false = failure
 *-----------------------------------------------------------------------------
 */
bool os_uninitialize()
{
	/*
	 * If we were in a simulated environment, then we will call
	 * Free the library that we had loaded during the execution of our app.
	 */
	if(g_simulation_mode) {

		FreeLibrary(g_lib_hdl);
	}
	if (user32_hmodule){
		FreeLibrary(user32_hmodule);
	}

	return (UNLOAD_DRIVER()) ? true : false;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_get_name
 * Parameters:
 *		OUT os_info_t *os_info
 * Description:
 *		This function gets the opearting system name
 * Returns:
 *		bool -
 *			true  = success
 *			false = failure
 *-----------------------------------------------------------------------------
 */
bool os_get_name(OUT os_info_t *os_info)
{
	bool ret_code = true;
	OSVERSIONINFO verInfo;
	char name[MAX_SIZE];

	memset(&verInfo, 0, sizeof(OSVERSIONINFO));
	memset(os_info, 0, sizeof(os_info_t));
	verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	/* Get the OS Version */
	if(!GetVersionEx(&verInfo)) {

		display_error_message();
		ret_code = false;

	} else {

		if(verInfo.dwMajorVersion == 5 && verInfo.dwMinorVersion == 0) {

			sprintf(name, "Windows 2000: %s", verInfo.szCSDVersion);

		} else if(verInfo.dwMajorVersion == 5 &&
			verInfo.dwMinorVersion == 1) {

			sprintf(name, "Windows XP: %s", verInfo.szCSDVersion);

		} else {

			sprintf(name, "%s", verInfo.szCSDVersion);
		}

		sprintf(os_info->os_name, "OS NAME: %s", name);

	}

	return ret_code;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_get_system_memory
 * Parameters:
 *		OUT iegd_hal_info_t *args
 * Description:
 *		Get the system memory info
 * Returns:
 *		bool -
 *			true  = success
 *			false = failure
 *-----------------------------------------------------------------------------
 */
bool os_get_system_memory(OUT iegd_hal_info_t *args)
{
	bool ret_val = true;
	MEMORYSTATUS mem_status;

	GlobalMemoryStatus(&mem_status);


	sprintf(args->arg[0], "%-48s: %-4d %s",
		"Total Physical Memory",
		(DWORD) ceil((double) (mem_status.dwTotalPhys/1024/1024)), "MB");
	sprintf(args->arg[1], "%-48s: %-4d %s",
		"Available Physical Memory",
		(DWORD) ceil((double) mem_status.dwAvailPhys/1024/1024), "MB");
	sprintf(args->arg[2], "%-48s: %-4d %s",
		"Total Virtual Memory",
		(DWORD) ceil((double) mem_status.dwTotalVirtual/1024/1024), "MB");
	sprintf(args->arg[3], "%-48s: %-4d %s",
		"Available Virtual Memory",
		(DWORD) ceil((double) mem_status.dwAvailVirtual/1024/1024), "MB");
	sprintf(args->arg[4], "%-48s: %-4d %s",
		"Total Page file Memory",
		(DWORD) ceil((double) mem_status.dwTotalPageFile/1024/1024), "MB");
	sprintf(args->arg[5], "%-48s: %-4d %s",
		"Available Page file Memory",
		(DWORD) ceil((double) mem_status.dwAvailPageFile/1024/1024), "MB");
	sprintf(args->arg[6], "%-48s: %d %%",
		"Current Memory Load",
		mem_status.dwMemoryLoad);

	args->total_args_filled = 7;

	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_get_cpu_info
 * Parameters:
 *		OUT iegd_hal_info_t *args
 * Description:
 *		Get the CPU info
 * Returns:
 *		bool -
 *			true  = success
 *			false = failure
 *-----------------------------------------------------------------------------
 */
bool os_get_cpu_info(OUT iegd_hal_info_t *args)
{
	bool ret_val = true;
	unsigned long reg_exit_code, type, data_size = MAX_SIZE, disposition;
	BYTE data[MAX_SIZE];
	char mod_data[MAX_SIZE];
	HKEY h_key;
	OSVERSIONINFO verInfo;

	args->total_args_filled = 0;

	/* Open the registry key*/
	reg_exit_code = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
		CPU_INFO_REG,
		0,
		NULL,
		REG_OPTION_NON_VOLATILE,
		KEY_READ,
		NULL,
		&h_key,
		&disposition);

	if(reg_exit_code == ERROR_SUCCESS) {

		memset(&verInfo, 0, sizeof(OSVERSIONINFO));
		verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		if(!GetVersionEx(&verInfo)) {

			display_error_message();
			ret_val = false;

		} else {

			if(verInfo.dwMajorVersion != 4) {
				/* Get the CPU identifier */
				reg_exit_code = RegQueryValueEx(h_key,
					CPU_IDENTIFIER,
					0,
					&type,
					data,
					&data_size);

				if(reg_exit_code == ERROR_SUCCESS) {

					sprintf(args->arg[args->total_args_filled++], "%-48s: %s",
						"CPU Identifier",
						(char *) data);

					data_size = MAX_SIZE;
					/* Get the processor name string*/
					reg_exit_code = RegQueryValueEx(h_key,
						CPU_PROC_NAME_STR,
						0,
						&type,
						data,
						&data_size);

					if(reg_exit_code == ERROR_SUCCESS) {

						remove_leading_ending_spaces((char *)data, mod_data);
						sprintf(args->arg[args->total_args_filled++],
							"%-48s: %s", "Processor Name String", mod_data);

						data_size = MAX_SIZE;
						/* Get the CPU vendor identifier */
						reg_exit_code = RegQueryValueEx(h_key,
							CPU_VEND_ID,
							0,
							&type,
							data,
							&data_size);

						if(reg_exit_code == ERROR_SUCCESS) {

							sprintf(args->arg[args->total_args_filled++],
								"%-48s: %s", "CPU Vendor ID", (char *) data);

						} else {

							display_error_message();
							WRITE_MSG(0, (MSG_ERROR,
								"Couldn't open a handle to %s",
								CPU_VEND_ID));
							ret_val = false;

						}
					} else {

						display_error_message();
						WRITE_MSG(0, (MSG_ERROR,
							"Couldn't open a handle to %s",
							CPU_PROC_NAME_STR));
						ret_val = false;

					}
				} else {

					display_error_message();
					WRITE_MSG(0, (MSG_ERROR, "Couldn't open a handle to %s",
						CPU_IDENTIFIER));
					ret_val = false;

				}

				RegCloseKey(h_key);
			}
		}

	} else {

		display_error_message();
		WRITE_MSG(0, (MSG_ERROR, "Couldn't open a handle to %s",
			CPU_INFO_REG));
		ret_val = false;
	}

	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_send_escape
 * Parameters:
 *		IN int escape - escape command to send
 *		IN int input_size - The input parameter size in bytes
 *		IN char *input - Input parameter to the escape API
 *		IN int output_size - The output paramater size in bytes
 *		OUT char *output - Output parameter to the escape API
 * Description:
 *		This function sends an escape command to the display driver
 * Returns:
 *		int -
 *			INTEL_ESCAPE_NOT_SUPPORTED	-1
 *			INTEL_ESCAPE_SUCCESS		0
 *			INTEL_ESCAPE_ERROR			1
 *-----------------------------------------------------------------------------
 */
int os_send_escape(
	IN int escape,
	IN int input_size,
	IN char *input,
	IN int output_size,
	OUT char *output)
{
	int ret_val = INTEL_ESCAPE_SUCCESS;
	bool found_intel_disp = false;
	ESCAPE_FUNC escape_func_ptr;
	unsigned long index;
	DISPLAY_DEVICE disp_device;

	/*
	 * If we are in a simulated environment, then we will call
	 * the dll's function pointers, otherwise we call Windows API
	 */
	if(g_simulation_mode) {

		int extension = 0;

		escape_func_ptr = (ESCAPE_FUNC) GetProcAddress(g_lib_hdl,
			ESCAPE_FUNC_NAME);

		if(escape_func_ptr(extension, escape,
			input_size, input,
			output_size, output) <= 0) {

			display_error_message();
			ret_val = INTEL_ESCAPE_ERROR;
		}

	} else {

		HDC h_dc;
		char disp_array[MAX_DISPLAYS][MAX_SIZE];
		unsigned long size = 0;

		for(index = 0; index < MAX_DISPLAYS; index++) {

			ZeroMemory(&disp_device, sizeof(disp_device));
			disp_device.cb = sizeof(disp_device);
			if(EnumDisplayDevices(NULL, index, &disp_device, 0)) {

				if(strprefix(disp_device.DeviceString, INTEL_CORP) == 0 &&
					(strstr(disp_device.DeviceString, IEGD_STRING) != NULL) ||
					(strstr(disp_device.DeviceString, IEGD_STRING2) != NULL)) {

					strcpy(disp_array[size++], disp_device.DeviceName);
				}
			}
		}

		/*
		 * If we didn't find a display device that is controlled by IEGD, then
		 * time to bail out.
		 */
		if(!size) {

			ret_val = INTEL_ESCAPE_ERROR;
			WRITE_MSG(0, (MSG_ERROR,
				"ERROR: Can't find IEGD installed on this system"));

		} else {

			for(index = 0; index < size; index++) {
#if 1
				h_dc = CreateDC(disp_array[index],
					disp_array[index], NULL, NULL);
#else
				h_dc = CreateDC("DISPLAY", NULL, NULL, NULL);
#endif
				if(!h_dc) {

					display_error_message();
					ret_val = INTEL_ESCAPE_ERROR;

				} else {

					ret_val = ExtEscape(h_dc, escape, input_size,
						input, output_size, output);
					if(ret_val <= 0) {

						ret_val = INTEL_ESCAPE_ERROR;

					} else {

						ret_val = INTEL_ESCAPE_SUCCESS;
					}

					DeleteDC(h_dc);

					/*
					 * If the very first call succeeded, then no need to go to
					 * another one
					 */
					if(ret_val == INTEL_ESCAPE_SUCCESS) {

						break;
					}
				}
			}
		}
	}
	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_wait
 * Parameters:
 *		IN unsigned long num_seconds - The number of seconds to wait for
 * Description:
 *		This function makes the program go to sleep for the specified
 *		number of seconds. This function can be used when running a script
 *		and the user requires to see display changes before the next
 *		command is run.
 * Returns:
 *		bool -
 *			true  = success
 *			false = failure
 *-----------------------------------------------------------------------------
 */
bool os_wait(IN unsigned long num_seconds)
{
	HANDLE event_hdl;
	unsigned long sleep_counter;

	event_hdl = CreateEvent(NULL, TRUE, FALSE, NULL);

	WRITE_MSG(1, (MSG_INFO, "Waiting for: %5ld seconds\n\n"));

	for(sleep_counter = num_seconds; sleep_counter > 0; sleep_counter--) {

		/*
		 * We will enter an efficient wait state. This is much
		 * better than putting a Sleep here which does busy waiting and
		 * keeps taking processor time.
		 */
		WaitForSingleObject(event_hdl, 1000);
	}

	CloseHandle(event_hdl);

	return true;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		extract_resource
 * Parameters:
 *		IN char *resource_name - the name of the resource to extract
 *		IN HRSRC h_rsrs - the handle to the resource
 * Description:
 *		This function dumps a resource in the current directory
 * Returns:
 *		BOOL -
 *			TRUE = success
 *			FALSE = failure
 *-----------------------------------------------------------------------------
 */
BOOL extract_resource(IN char *resource_name, IN HRSRC h_rsrs)
{
	if(!h_rsrs) {
		display_error_message();
		return FALSE;
	}

	HGLOBAL h_global = LoadResource(NULL, h_rsrs);
	DWORD dw_size = SizeofResource(NULL, h_rsrs);
	LPVOID lp_data = LockResource(h_global);

	if (!h_global || !lp_data)
	{
		display_error_message();
		return FALSE;
	}

	//create the empty file.
	HANDLE h_file = CreateFile(resource_name, GENERIC_WRITE, 0, 0,
		CREATE_ALWAYS, 0, 0);
	if (h_file == INVALID_HANDLE_VALUE)
	{
		display_error_message();
		return FALSE;
	}

	//write it out
	DWORD dw_bytes_written = 0;
	BOOL b_file_written = WriteFile(h_file, lp_data, dw_size,
		&dw_bytes_written, NULL);

	//if we failed to write, or didn't write the whole thing, fail.
	if ((!b_file_written) || (dw_size != dw_bytes_written))
	{
		display_error_message();
		return FALSE;
	}

	CloseHandle(h_file);

	return TRUE;
}


/*-----------------------------------------------------------------------------
 * Function:
 *		display_error_message
 * Parameters:
 *		NONE
 * Description:
 *		Displays an error message using GetLastError API
 * Returns:
 *		void
 *-----------------------------------------------------------------------------
 */
void display_error_message()
{
	LPVOID lpMsgBuf;
	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		GetLastError(),
		0, // Default language
		(LPTSTR) &lpMsgBuf,
		0,
		NULL
	);
	// Process any inserts in lpMsgBuf.
	// ...
	// Display the string.
	WRITE_MSG(0, (MSG_INFO, "%s", (LPCTSTR)lpMsgBuf));

	// Free the buffer.
	LocalFree( lpMsgBuf );
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_change_disp_settings
 * Parameters:
		IN iegd_esc_set_dc_t *set_dc---new display mode structure
 *		IN bool was_extended_mode - whether we were in extended mode or not
 *		IN scrn_info_t scrn_info- pointer to screen info structure
  * Description:
 *		This function changes the display settings and gets Windows OS in
 *		sync with the driver's new settings.
 * Returns:
 *		bool -
 *			true  = success
 *			fales = failure
 *-----------------------------------------------------------------------------
 */
bool os_change_disp_settings(
	IN iegd_esc_set_dc_t *set_dc,
	IN bool was_extended_mode,
	IN scrn_info_t *scrn_info)
{
	bool ret_val = true;
	unsigned long sec_disp_num;

	/* If the new display mode is SINGLE, TWIN or CLONE */
	if(IS_SINGLE_MODE(set_dc->dc) ||
		IS_CLONE_MODE(set_dc->dc) ||
		IS_TWIN_MODE(set_dc->dc)) {
		/*
		 * ...and we were in extended mode previously, then we need to detach
		 * the secondary display
		 */
		if(was_extended_mode) {
			/* check which display is secondary for windows */
			if( scrn_info->is_primary[PRIMARY_DISP]) {
				sec_disp_num = scrn_info->scrn_num[SECONDARY_DISP];
			} else {
				sec_disp_num = scrn_info->scrn_num[PRIMARY_DISP];
			}
			detach_display(sec_disp_num);
			return ret_val;
		}

		/*
		 * The last parameter in this function indicates that we want to force
		 * a reset.
		 */
		if(scrn_info->chng_flag & PRIMARY_CHANGED) {
			ret_val = os_set_scrn_res(scrn_info->scrn_num[PRIMARY_DISP],
				set_dc->iegd_timings[0].width,
				set_dc->iegd_timings[0].height, set_dc->iegd_timings[0].refresh,
				set_dc->iegd_fb_info[0].bit_depth, 0, 1);
		}
	
	} else if(IS_EXTENDED_MODE(set_dc->dc) && !was_extended_mode) {

		/*
		 * The above if says if the new display mode is EXTENDED and we were
		 * not in extended mode before this change happened.
		 * In such a situation we need to extend the desktop through the OS
		 * APIs
		 */
		/* check which display is secondary for windows  */
		if( scrn_info->is_primary[PRIMARY_DISP]) {
			sec_disp_num = scrn_info->scrn_num[SECONDARY_DISP];
		} else {
			sec_disp_num = scrn_info->scrn_num[PRIMARY_DISP];
		}

		ret_val = extend_desktop(set_dc->iegd_timings[1].width,
			set_dc->iegd_timings[1].height, set_dc->iegd_timings[1].refresh,
			set_dc->iegd_fb_info[1].bit_depth, sec_disp_num);

		if(ret_val) {

			//ret_val = os_set_scrn_res(0, width, height, refresh,bpp);
			if(scrn_info->chng_flag & PRIMARY_CHANGED){
				ret_val = os_set_scrn_res(scrn_info->scrn_num[PRIMARY_DISP],
					set_dc->iegd_timings[0].width,
					set_dc->iegd_timings[0].height,
					set_dc->iegd_timings[0].refresh,
					set_dc->iegd_fb_info[0].bit_depth, 0, 1);
			}


		}
	} else {

		/*
		 * else we could be going in this case if we were in extended mode
		 * before and are also in extended mode now
		 * In this case, all we need to do is a set mode
		 */
		if(scrn_info->chng_flag & PRIMARY_CHANGED){
			ret_val = os_set_scrn_res(scrn_info->scrn_num[PRIMARY_DISP],
				set_dc->iegd_timings[0].width,
				set_dc->iegd_timings[0].height, set_dc->iegd_timings[0].refresh,
				set_dc->iegd_fb_info[0].bit_depth, 0, 1);
		}
		if(scrn_info->chng_flag & SECONDARY_CHANGED){
			ret_val = os_set_scrn_res(scrn_info->scrn_num[SECONDARY_DISP],
				set_dc->iegd_timings[1].width,
				set_dc->iegd_timings[1].height, set_dc->iegd_timings[1].refresh,
				set_dc->iegd_fb_info[1].bit_depth, 0, 1);
		}


	}

	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		os_set_scrn_res
 * Parameters:
 *		IN unsigned long screen_num - The screen number to set the width of
 *		IN unsigned short width - The new width to set
 *		IN unsigned short height - The new height to set
 *		IN unsigned short refresh - The new refresh rate to set
 *		IN unsigned short bpp - The new bits per pixel to set
 *		IN unsigned long rotation - New rotation value
 *		IN unsigned long force_reset - Force a reset if 1
 * Description:
 *		This function sets a particular screens resolution to some
 *		user specified value
 * Returns:
 *		bool -
 *			true  = success
 *			fales = failure
 *-----------------------------------------------------------------------------
 */
bool os_set_scrn_res(
	IN unsigned long screen_num,
	IN unsigned short width,
	IN unsigned short height,
	IN unsigned short refresh,
	IN unsigned short bpp,
	IN unsigned long rotation,
	IN unsigned long force_reset)
{
	DISPLAY_DEVICE disp_device;
	DEVMODE default_mode, current_mode;
	bool ret_val = true;
	unsigned long disp_array[MAX_DISPLAYS], index, size, cur_bpp;
	unsigned short saved_width = width, saved_height = height,
		saved_refresh = refresh;
	DISP_SETTINGS_FUNC change_disp_settings_ptr;

	/* Initialize disp_device */
	ZeroMemory(&disp_device, sizeof(disp_device));
	disp_device.cb = sizeof(disp_device);

	size = 0;

	for(index = 0; index < MAX_DISPLAYS; index++) {

		ZeroMemory(&disp_device, sizeof(disp_device));
		disp_device.cb = sizeof(disp_device);
		if(EnumDisplayDevices(NULL, index, &disp_device, 0)) {

			if(strprefix(disp_device.DeviceString, INTEL_CORP) == 0 &&
				(strstr(disp_device.DeviceString, IEGD_STRING) != NULL) ||
				(strstr(disp_device.DeviceString, IEGD_STRING2) != NULL)) {

				disp_array[size++] = index;
			}
		}
	}

	/*
	 * If the user gave us a particular display then we should only change
	 * the mode settings for that display
	 */
	if(screen_num != ALL_DISPLAYS) {

		if(screen_num < size) {

			size = 1;
			/* Put the n'th display number as the first one */
			disp_array[0] = disp_array[screen_num];
		} else {

			WRITE_MSG(0, (MSG_ERROR, "ERROR: Invalid screen number"));
			ret_val = false;
		}
	}

	for(index = 0; index < size && ret_val; index++) {

		ZeroMemory(&disp_device, sizeof(disp_device));
		disp_device.cb = sizeof(disp_device);
		width   = saved_width;
		height  = saved_height;
		refresh = saved_refresh;

		/* Get display devices. */
		if(EnumDisplayDevices(NULL, disp_array[index], &disp_device, 0)) {

			/*
			 * The code below has to be done for rotation. Windows seems to
			 * like this if we are going to replace landscape<->portait modes.
			 * Also we need to find out if we support 24 or 32 bpp
			 */
			/*****************************************************************/
			int mode_num = 0;
			unsigned long bpp_high = 0;
			DEVMODE dm_enum;

			while (1) {

				memset(&dm_enum, 0, sizeof(DEVMODE));
				dm_enum.dmSize = sizeof(DEVMODE);
				dm_enum.dmDriverExtra = 0;

				if(!EnumDisplaySettings(
					disp_device.DeviceName,
					mode_num,
					&dm_enum)){

						break;
				}

				if(dm_enum.dmBitsPerPel > bpp_high) {

					bpp_high = dm_enum.dmBitsPerPel;
				}

				mode_num++;
			}
			/*****************************************************************/

			ZeroMemory(&default_mode, sizeof(DEVMODE));
			ZeroMemory(&current_mode, sizeof(DEVMODE));
			default_mode.dmSize = sizeof(DEVMODE);

			/*
			 * If the width, height and refresh are all 0, then this means
			 * that we need to get the default width, height and refresh
			 */
			if(width == 0 && height == 0 && refresh == 0) {

				/* Figure out the default width, height and refresh */
				if(EnumDisplaySettings(disp_device.DeviceName,
					ENUM_CURRENT_SETTINGS, &current_mode)) {

					if(rotation == 90 || rotation == 270) {

						width   = (unsigned short) current_mode.dmPelsHeight;
						height  = (unsigned short) current_mode.dmPelsWidth;
					} else {

						width   = (unsigned short) current_mode.dmPelsWidth;
						height  = (unsigned short) current_mode.dmPelsHeight;
					}
					refresh = (unsigned short) current_mode.dmDisplayFrequency;
				}
			}

			if(bpp == 0) {

				/* Figure out the default width, height and refresh */
				if(EnumDisplaySettings(disp_device.DeviceName,
					ENUM_CURRENT_SETTINGS, &current_mode)) {

					bpp = (unsigned short) current_mode.dmBitsPerPel;
				}

			} else if(bpp != 8 && bpp != 16 && bpp != 32) {

				/*
				 * If the user didn't specify the right bits per pixel, then we
				 * will default to 32
				 */
				bpp = 32;
			}
			//EnumDisplaySettings(disp_device.DeviceName,
			//		ENUM_CURRENT_SETTINGS, &default_mode);
			default_mode.dmPelsWidth = width;
			default_mode.dmPelsHeight = height;
			default_mode.dmDisplayFrequency = refresh;
			default_mode.dmBitsPerPel = bpp;
			default_mode.dmFields = DM_PELSWIDTH |
				DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;

			WRITE_MSG(1, (MSG_INFO, "New width = %d",
				default_mode.dmPelsWidth));
			WRITE_MSG(1, (MSG_INFO, "New height = %d",
				default_mode.dmPelsHeight));
			WRITE_MSG(1, (MSG_INFO, "New Freq = %d",
				default_mode.dmDisplayFrequency));
			WRITE_MSG(1, (MSG_INFO, "New bpp = %d",
				default_mode.dmBitsPerPel));

			/*
			 * If we are switching from 0<->180 or 90<->270, OR force reset was
			 * called we need to switch to an intermediate mode. So change
			 * depth.
			 */
			/*****************************************************************/
				/* Figure out the default width, height and refresh */

			EnumDisplaySettings(disp_device.DeviceName,
					ENUM_CURRENT_SETTINGS, &current_mode);

			if(rotation == 180 || force_reset) {

				cur_bpp = default_mode.dmBitsPerPel;
				if(current_mode.dmBitsPerPel == 16) {

					default_mode.dmBitsPerPel = bpp_high;

				} else if(current_mode.dmBitsPerPel == bpp_high ||
						default_mode.dmBitsPerPel == 0) {

					default_mode.dmBitsPerPel = 16;
				}

				/*
				 * If we are in a simulated environment, then we will call
				 * the dll's function pointers, otherwise we call Windows API
				 */
				if(g_simulation_mode) {

					change_disp_settings_ptr = (DISP_SETTINGS_FUNC)
						GetProcAddress(g_lib_hdl, DISP_SETTINGS_FUNC_NAME);

					if(!change_disp_settings_ptr(&default_mode, CDS_RESET)) {

						ret_val = false;
						break;
					}
				} else {

					long cd_ret_val;
					cd_ret_val = 
						do_change_display_settings(disp_device.DeviceName,
						&default_mode,
						(CDS_RESET | CDS_UPDATEREGISTRY |CDS_GLOBAL));

					default_mode.dmBitsPerPel = cur_bpp;
				}
			}
			/*****************************************************************/


			if(g_simulation_mode) {

				change_disp_settings_ptr = (DISP_SETTINGS_FUNC)
					GetProcAddress(g_lib_hdl, DISP_SETTINGS_FUNC_NAME);

				if(!change_disp_settings_ptr(&default_mode, CDS_NORESET)) {

					ret_val = false;
					break;
				}
			} else {

				long cd_ret_val1 =
					do_change_display_settings(disp_device.DeviceName,
						&default_mode,
						(CDS_NORESET | CDS_UPDATEREGISTRY |CDS_GLOBAL));

				display_error_message();
				/*
				 * A second call to ChangeDisplaySettings updates the monitor.
				 */
				long cd_ret_val2 =
				do_change_display_settings(disp_device.DeviceName,NULL,NULL);
				display_error_message();
			}
		} else {

			ret_val = false;
			break;
		}
	}

	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *		extend_desktop
 * Parameters:
 *		IN unsigned short width - The new width to set
 *		IN unsigned short height - The new height to set
 *		IN unsigned short refresh - The new refresh rate to set
		IN unsigned short bpp - the new bpp to set
		IN unsigned long - the relative display number of the display that needs
			to be extended to
 * Description:
 *		This function extends the desktop to a secondary device
 * Returns:
 *		bool -
 *			true  = success
 *			fales = failure
 *-----------------------------------------------------------------------------
 */
bool extend_desktop(
	IN unsigned short new_width,
	IN unsigned short new_height,
	IN unsigned short new_refresh,
	IN unsigned short new_bpp,
	IN unsigned long rel_disp_num)
{
	DWORD disp_num = 0;
	DISPLAY_DEVICE disp_device;
	DEVMODE default_mode;
	HDC hdc;
	int width;
	bool found_secondary = false, ret_val = true;
	DISP_SETTINGS_FUNC change_disp_settings_ptr;
	unsigned long size ,index, disp_array[MAX_DISPLAYS];

	hdc = GetDC(0);
	width = GetDeviceCaps(hdc,HORZRES);
	ReleaseDC(0,hdc);

	size = 0;

	for(index = 0; index < MAX_DISPLAYS; index++) {

		ZeroMemory(&disp_device, sizeof(disp_device));
		disp_device.cb = sizeof(disp_device);
		if(EnumDisplayDevices(NULL, index, &disp_device, 0)) {

			if(strprefix(disp_device.DeviceString, INTEL_CORP) == 0 &&
				(strstr(disp_device.DeviceString, IEGD_STRING) != NULL) ||
				(strstr(disp_device.DeviceString, IEGD_STRING2) != NULL)) {

				disp_array[size++] = index;
			}
		}
	}


	// Get display devices.
	if(EnumDisplayDevices(NULL, disp_array[rel_disp_num], &disp_device, 0)) {

		ZeroMemory(&default_mode, sizeof(DEVMODE));
		default_mode.dmSize = sizeof(DEVMODE);
		int mode_num = 0;

		/*
		 * this while loop is required to force OS to call query modes
		 * when change_display(settings() is called
		 */

		while(1) {

			memset(&default_mode, 0, sizeof(DEVMODE));
			default_mode.dmSize = sizeof(DEVMODE);
			default_mode.dmDriverExtra = 0;

			if(!EnumDisplaySettings(
				disp_device.DeviceName,
				mode_num,
				&default_mode)) {

					break;
			}
			mode_num++;
		}

		if(!EnumDisplaySettings((LPSTR)disp_device.DeviceName,
			ENUM_REGISTRY_SETTINGS, &default_mode)) {

			ret_val = false; // Store default failed
		}

		if(!(disp_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
			//Found the first secondary device.
			default_mode.dmPosition.x += width;
			default_mode.dmPelsWidth = new_width;
			default_mode.dmPelsHeight = new_height;
			default_mode.dmDisplayFrequency = new_refresh;
			default_mode.dmFields = DM_POSITION | DM_PELSWIDTH |
				DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_BITSPERPEL;
			default_mode.dmBitsPerPel = new_bpp;

			if(g_simulation_mode) {

				change_disp_settings_ptr = (DISP_SETTINGS_FUNC)
					GetProcAddress(g_lib_hdl, DISP_SETTINGS_FUNC_NAME);

				if(!change_disp_settings_ptr(&default_mode, CDS_NORESET)) {

					ret_val = false;
				}
			} else {

				do_change_display_settings(disp_device.DeviceName,&default_mode,
					(CDS_NORESET | CDS_UPDATEREGISTRY |CDS_GLOBAL));

				// A second call to ChangeDisplaySettings updates the monitor.
				do_change_display_settings(NULL,NULL,NULL);
			}
		}
	}
	return ret_val;
}


/*-----------------------------------------------------------------------------
 * Function:
 *		detach_display
 * Parameters:
 *		unsigned long rel_disp_num, disp num of the device that has to be
 *		detached.
 * Description:
 *		This function detaches secondary display with the rel_disp_num 
 *		that may be connected.
 * Returns:
 *		void
 *-----------------------------------------------------------------------------
 */
void detach_display(IN unsigned long rel_disp_num)
{
	DWORD           disp_num = 0;
	DISPLAY_DEVICE  disp_device;
	LONG            result;
	DEVMODE         default_mode;
	DISP_SETTINGS_FUNC change_disp_settings_ptr;
	unsigned long size ,index, disp_array[MAX_DISPLAYS];

	size = 0;

	for(index = 0; index < MAX_DISPLAYS; index++) {

		ZeroMemory(&disp_device, sizeof(disp_device));
		disp_device.cb = sizeof(disp_device);
		if(EnumDisplayDevices(NULL, index, &disp_device, 0)) {

			if(strprefix(disp_device.DeviceString, INTEL_CORP) == 0 &&
				(strstr(disp_device.DeviceString, IEGD_STRING) != NULL) ||
				(strstr(disp_device.DeviceString, IEGD_STRING2) != NULL)) {

				disp_array[size++] = index;
			}
		}
	}

	// get the requested display device
	if(EnumDisplayDevices(NULL, disp_array[rel_disp_num], &disp_device, 0)) {

		ZeroMemory(&default_mode, sizeof(DEVMODE));
		default_mode.dmSize = sizeof(DEVMODE);

		if(!EnumDisplaySettings((LPSTR)disp_device.DeviceName,
			ENUM_REGISTRY_SETTINGS, &default_mode)) {

			OutputDebugString("Store default failed\n");
		}

		if((disp_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
			!(disp_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {

			DEVMODE    dev_mode;
			ZeroMemory(&dev_mode, sizeof(dev_mode));

			dev_mode.dmSize = sizeof(dev_mode);
			dev_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL
				| DM_POSITION | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;

			if(g_simulation_mode) {

				change_disp_settings_ptr = (DISP_SETTINGS_FUNC)
					GetProcAddress(g_lib_hdl, DISP_SETTINGS_FUNC_NAME);

				change_disp_settings_ptr(&dev_mode, CDS_NORESET);
			} else {
				result = do_change_display_settings(disp_device.DeviceName,
						&dev_mode,CDS_UPDATEREGISTRY);

				result = do_change_display_settings(disp_device.DeviceName,NULL
							,NULL);
			}
		}
	}
}

/*-----------------------------------------------------------------------------
 * Function:
 *	os_get_full_path
 * Parameters:
 *	IN const char *file - The file name to find the path for
 *	OUT char *full_path - The path found
 * Description:
 *	This function finds the entire path of a file and returns it to the
 *	caller in the second parameter passed to it.
 * Returns:
 *	void
 *-----------------------------------------------------------------------------
 */
void os_get_full_path(IN const char *file, OUT char *full_path)
{
	char system_info[MAX_SIZE*4];
	DWORD result;

	result = ExpandEnvironmentStrings(
		"PATH=%PATH%",
		system_info,
		MAX_SIZE*4);

	if(!result) {

		display_error_message();
	}
//	_fullpath(full_path, file, MAX_SIZE);
}

/*-----------------------------------------------------------------------------
 * Function:
 *	os_set_mode
 * Parameters:
 *	IN unsigned short width   - The width to set
 *	IN unsigned short height  - The height to set
 *	IN unsigned short refresh - The refresh rate to set
 * Description:
 *	This function sets a mode on a display
 * Returns:
 *	bool -
 *		true  = success
 *		fales = failure
 *-----------------------------------------------------------------------------
 */
bool os_set_mode(
	IN unsigned short width,
	IN unsigned short height,
	IN unsigned short refresh)
{
	bool ret_val = true;
	DEVMODE dev_mode;
	DISP_SETTINGS_FUNC change_disp_settings_ptr;

	/*
	 * If we are in a simulated environment, then we will call
	 * the dll's function pointers, otherwise we call Windows API
	 */
	if(g_simulation_mode) {

		change_disp_settings_ptr = (DISP_SETTINGS_FUNC)
			GetProcAddress(g_lib_hdl, DISP_SETTINGS_FUNC_NAME);

		if(!change_disp_settings_ptr(&dev_mode, CDS_RESET)) {

			ret_val = false;
		}
	} else {

		/* Set values for the device mode */
		memset(&dev_mode, 0, sizeof(DEVMODE));
		dev_mode.dmSize = sizeof(DEVMODE);
		dev_mode.dmDriverExtra = 0;
		dev_mode.dmPelsWidth = width;
		dev_mode.dmPelsHeight = height;
		dev_mode.dmDisplayFrequency = refresh;
		dev_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT
			| DM_DISPLAYFREQUENCY;

		if(!ChangeDisplaySettings(&dev_mode,
			CDS_NORESET | CDS_UPDATEREGISTRY)) {

				ret_val = false;
		}

		// A second call to ChangeDisplaySettings updates the monitor.
		if(!ChangeDisplaySettings(NULL, 0)) {

			ret_val = false;
		}
	}

	return ret_val;
}

/*-----------------------------------------------------------------------------
 * Function:
 *	os_get_intel_displays
 * Parameters:
 *	OUT disp_port_info_t *disp_port_array
 *	OUT unsigned long *disp_port_array_size
 * Description:
 *
 * Returns:
 *  void
 *-----------------------------------------------------------------------------
 */
void os_get_intel_displays(
	OUT disp_port_info_t *disp_port_array,
	OUT unsigned long *disp_port_array_size)
{
	unsigned long index;
	DISPLAY_DEVICE disp_device;

	*disp_port_array_size = 0;

	for(index = 0; index < MAX_DISPLAYS; index++) {

		ZeroMemory(&disp_device, sizeof(disp_device));
		disp_device.cb = sizeof(disp_device);
		if(EnumDisplayDevices(NULL, index, &disp_device, 0)) {

			if(strprefix(disp_device.DeviceString, INTEL_CORP) == 0 &&
				(strstr(disp_device.DeviceString, IEGD_STRING) != NULL) ||
				(strstr(disp_device.DeviceString, IEGD_STRING2) != NULL)) {

				disp_port_array[*disp_port_array_size].abs_display_num = index;
				disp_port_array[*disp_port_array_size].rel_display_num =
					*disp_port_array_size;

				disp_port_array[(*disp_port_array_size)++].is_primary =
					(disp_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
					? true : false;
			}
		}
	}
}

/*-----------------------------------------------------------------------------
 * Function:
 *	os_get_scrn_res
 * Parameters:
 *	IN unsigned long screen_num - The screen number whose display info we need
 *	OUT unsigned long *width - Width of the display
 *	OUT unsigned long *height - Height of the display
 *	OUT unsigned long *refresh - Refresh of the display
 *	OUT unsigned long *bpp - Bits Per Pixel of the display
 *	OUT unsigned long *rotation - Rotation info of the display
 * Description:
 *	This function returns the current screen resolution of a particular display
 *	according to the OS.
 * Returns:
 *	bool -
 *		true  = success
 *		false = failure
 *-----------------------------------------------------------------------------
 */
bool os_get_scrn_res(
	IN unsigned long screen_num,
	OUT unsigned long *width,
	OUT unsigned long *height,
	OUT unsigned long *refresh,
	OUT unsigned long *bpp,
	OUT unsigned long *rotation)
{
	DISPLAY_DEVICE disp_device;
	DEVMODE current_mode;
	bool ret_val = true;
	unsigned long disp_array[MAX_DISPLAYS], index, size;

	/* Initialize disp_device */
	ZeroMemory(&disp_device, sizeof(disp_device));
	disp_device.cb = sizeof(disp_device);

	size = 0;

	for(index = 0; index < MAX_DISPLAYS; index++) {

		ZeroMemory(&disp_device, sizeof(disp_device));
		disp_device.cb = sizeof(disp_device);
		if(EnumDisplayDevices(NULL, index, &disp_device, 0)) {

			if(strprefix(disp_device.DeviceString, INTEL_CORP) == 0 &&
				(strstr(disp_device.DeviceString, IEGD_STRING) != NULL) ||
				(strstr(disp_device.DeviceString, IEGD_STRING2) != NULL)) {

				disp_array[size++] = index;
			}
		}
	}

	if(screen_num >= size) {

		WRITE_MSG(0, (MSG_ERROR, "ERROR: Invalid screen number"));
		return false;
	}

	ZeroMemory(&disp_device, sizeof(disp_device));
	disp_device.cb = sizeof(disp_device);

	/* Get display devices. */
	if(EnumDisplayDevices(NULL, disp_array[screen_num], &disp_device, 0)) {

		/* Figure out the default width, height and refresh */
		if(EnumDisplaySettings(disp_device.DeviceName,
			ENUM_CURRENT_SETTINGS, &current_mode)) {

			*width   = (unsigned long) current_mode.dmPelsHeight;
			*height  = (unsigned long) current_mode.dmPelsWidth;
			*refresh = (unsigned long) current_mode.dmDisplayFrequency;
			*bpp     = (unsigned long) current_mode.dmBitsPerPel;

			switch(current_mode.dmDisplayOrientation) {
			case DMDO_90:
				*rotation = 90;
				break;
			case DMDO_180:
				*rotation = 180;
				break;
			case DMDO_270:
				*rotation = 270;
				break;
			case DMDO_DEFAULT:
			default:
				*rotation = 0;
				break;
			}
		} else {

			ret_val = false;
		}
	} else {

		ret_val = false;
	}

	return ret_val;
}

/*----------------------------------------------------------------------------
 * Function: do_change_display_settings()
 * Description: Makes a call t either pfnChangeDisplaySettingsEx or 
 *              ChangeDisplaySettings OS calls to set the display settings
 *              of current mode of display device. Look at MSDN
 *              for more information.
 * Parameters:
 * Returns:
 *
 *----------------------------------------------------------------------------
 */

long do_change_display_settings(
		char * dev_name,
		DEVMODE *new_mode_ptr,
		DWORD flags)
{
	long ret_val;

	if (pfnChangeDisplaySettingsEx) {
		ret_val=pfnChangeDisplaySettingsEx(dev_name,new_mode_ptr,NULL,flags,0);
	} else {
		ret_val = ChangeDisplaySettings(new_mode_ptr,
					flags);
	}
	return ret_val;
}
